package com.zfast.forge.game;

import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import com.zfast.forge.constant.SysConstant;
import com.zfast.forge.navigation.Coordinate;
import com.zfast.forge.pojo.CustomBigDecimal;
import com.zfast.forge.pojo.bo.PlayerInstance;
import com.zfast.forge.pojo.dto.Field;
import com.zfast.forge.pojo.dto.FieldLayer;
import com.zfast.forge.pojo.dto.FieldSurface;
import com.zfast.forge.qlExpress.FormulaEnum;
import com.zfast.forge.qlExpress.Math3Util;
import com.zfast.forge.qlExpress.QLExpressUtil;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author zgn
 * @Description: Game
 * @date 2024/3/1 0001
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(value = "Game", description = "游戏对局")
@Slf4j
public class Game {
    @ApiModelProperty("玩家实例（Player Instance）")
    List<PlayerInstance> teams;

    //游戏数据（Game Data）
    @ApiModelProperty("游戏模式")
    int gameFormat;

    @ApiModelProperty("场地尺寸")
    Field field;

    @ApiModelProperty("场地边线")
    BigDecimal[] boundaries;

    @ApiModelProperty("分区图层")
    FieldLayer fieldLayer;

    @ApiModelProperty("羽毛球场分区")
    List<FieldSurface> fieldSurfaces;

    @ApiModelProperty("公式解析器")
    ExpressRunner runner;

    /**
     * 加载运算符和边界
     *
     * @param
     * @author zgn
     * @date 2024/3/5 0005
     */
    public void init() {
        //加载运算符
        runner = new ExpressRunner(true, false);
        QLExpressUtil.autoFunctionsBind(runner, FormulaEnum.MovingPoint);
        QLExpressUtil.autoFunctionsBind(runner, FormulaEnum.VelocityDecomposition);

        DefaultContext<String, Object> context = new DefaultContext();
        context.put("field", field);

        Object[] boundariesResluts = new Object[8];

        switch (gameFormat) {
            case 1:
                boundariesResluts = (Object[]) QLExpressUtil.execute(FormulaEnum.Boundary1, context, runner);
                break;
            case 2:
                boundariesResluts = (Object[]) QLExpressUtil.execute(FormulaEnum.Boundary2, context, runner);
                break;
        }

        boundaries = Arrays.stream(boundariesResluts)
                .map(element -> new BigDecimal(element.toString()))
                .toArray(BigDecimal[]::new);

        teams.stream()
                .forEach(temp -> temp.setRunner(runner));
    }


    /**
     * 依据球的点位判定是否出界(true:出界)
     *
     * @param result 球的点位
     * @param key    所在场地
     * @return boolean
     * @author zgn
     * @date 2024/3/1 0001
     */
    public boolean out(Coordinate result, int key) {
        switch (key) {
            //SysConstant.field_side
            case 1:
                //上下左右
                if (result.x.compareTo(boundaries[0]) == -1) {
                    log.info("左场地:上");
                    return true;
                }

                if (result.x.compareTo(boundaries[1]) == 1) {
                    log.info("左场地:下");
                    return true;
                }

                if (result.y.compareTo(boundaries[2]) == -1) {
                    log.info("左场地:左");
                    return true;
                }

                if (result.y.compareTo(boundaries[3]) == 1) {
                    log.info("左场地:右");
                    return true;
                }
                break;
            case 2:
                if (result.x.compareTo(boundaries[4]) == -1) {
                    log.info("右场地:右");
                    return true;
                }

                if (result.x.compareTo(boundaries[5]) == 1) {
                    log.info("右场地:下");
                    return true;
                }

                if (result.y.compareTo(boundaries[6]) == -1) {
                    log.info("右场地:左");
                    return true;
                }

                if (result.y.compareTo(boundaries[7]) == 1) {
                    log.info("右场地:右");
                    return true;
                }
                break;
        }
        return false;
    }

    /**
     * 判定羽毛球图层落区
     *
     * @param result 羽毛球点位
     * @param key    左右
     * @author zgn
     * @date 2024/3/4 0004
     */
    public void isPointInRectangle(Coordinate result, int key) {

        double right_width = field.getY1() + field.getY2() + field.getY3();

        for (int i = 0; i < fieldSurfaces.size(); i++) {
            FieldSurface fieldSurface = fieldSurfaces.get(i);

            BigDecimal top = new BigDecimal(fieldSurface.getX());
            BigDecimal bottom = new BigDecimal(fieldSurface.getX() + fieldSurface.getWidth());
            BigDecimal left = new BigDecimal(key == 1 ? fieldSurface.getY() : (2 * right_width - fieldSurface.getY() - fieldSurface.getLength()));
            BigDecimal right = new BigDecimal(key == 1 ? (fieldSurface.getY() + fieldSurface.getLength()) : (2 * right_width - fieldSurface.getY()));

            if (result.x.compareTo(top) == 1
                    && result.x.compareTo(bottom) == -1
                    && result.y.compareTo(left) == 1
                    && result.y.compareTo(right) == -1
            ) {
                log.info(String.format("落球区域:%s,%s", key == 1 ? "左" : "右", fieldSurface.getName()));
                break;
            }

        }
    }

    /**
     * 羽毛球追击相遇
     * @author  zgn
     * @date    2024/3/8 0008
     * @param attempt 接球
     * @param from 球来源的人
     */
    public BigDecimal pursuitMeeting(PlayerInstance attempt, PlayerInstance from) {
        //region 反应期间得分
        //zgnTodo 羽毛球竖直方向公式调整
        BigDecimal T落地 = Math3Util.verticalThrowMotion(from.getStand().z.doubleValue(), from.getSpeed().z.doubleValue(), from.getAcceleration().z.doubleValue());
        //T飞行:人反映前羽毛球飞行时间
        BigDecimal T飞行 = attempt.calculateRunTime();

        log.debug(String.format("T落地:%s;T飞行:%s;", T落地,T飞行));

        if(T飞行.compareTo(T落地)==1){
            log.info(String.format("%s击球落地", from.getName()));
            return T落地;
        }
        //endregion

        //region 无法追击,追击失败(落地,出界)
        Coordinate ballStart = from.calculateFinalPosition(T飞行);
        double v = attempt.getAge() / 20 * 2;
        BigDecimal T追击 = Math3Util.pursuitMeeting(ballStart, from.getSpeed(), from.getAcceleration(),attempt.getStand(),v);

        if(T追击.compareTo(BigDecimal.ZERO)==-1){
            return T追击;
        }

        BigDecimal T总 = T追击.add(T飞行);
        log.debug(String.format("T总:%s;", T总));
        if(T总.compareTo(T落地)==1){
            log.info(String.format("%s追击失败:落地", attempt.getName()));
            return new BigDecimal(-1);
        }

        log.info(String.format("%s可追击", attempt.getName()));
        //endregion
        return T总;
    }
}
